/*
 * Copyright (c) 2003-2007 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jme.intersection;

import java.util.ArrayList;

import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Ray;
import com.jme.math.Vector3f;
import com.jme.scene.batch.GeomBatch;
import com.jme.scene.batch.TriangleBatch;
import com.jme.util.LoggingSystem;

/**
 * Pick data for triangle accuracy picking including sort by distance to
 * intersection point.
 */
public class TrianglePickData extends PickData {
	private Vector3f[] worldTriangle;

	private Vector3f intersectionPoint;

	private Quaternion worldRotation;

	private Vector3f worldScale;

	private Vector3f worldTranslation;

	public TrianglePickData(Ray ray, GeomBatch targetMesh,
			ArrayList<Integer> targetTris, boolean checkDistance) {
		super(ray, targetMesh, targetTris, checkDistance);
	}

	protected float calculateDistance() {
		ArrayList<Integer> tris = getTargetTris();
		if (tris.isEmpty()) {
			return Float.POSITIVE_INFINITY;
		}

		TriangleBatch mesh = (TriangleBatch) getTargetMesh();

		mesh.getParentGeom().updateWorldVectors();
		worldRotation = mesh.getParentGeom().getWorldRotation();
		worldScale = mesh.getParentGeom().getWorldScale();
		worldTranslation = mesh.getParentGeom().getWorldTranslation();

		worldTriangle = new Vector3f[3];
		int i;
		for (i = 0; i < 3; i++) {
			worldTriangle[i] = new Vector3f();
		}
		intersectionPoint = new Vector3f();

		Vector3f[] vertices = new Vector3f[3];
		float distance = Float.MAX_VALUE;
		float[] distances = new float[tris.size()];
		for (i = 0; i < tris.size(); i++) {
			int triIndex = ((Integer) tris.get(i)).intValue();
			mesh.getTriangle(triIndex, vertices);
			float triDistance = getDistanceToTriangle(vertices);
			distances[i] = triDistance;
			if (triDistance > 0 && triDistance < distance) {
				distance = triDistance;
			}
		}
		
		//XXX optimize! ugly bubble sort for now
		boolean sorted = false;
		while(!sorted) {
			sorted = true;
			for(int sort = 0; sort < distances.length - 1; sort++) {
				if(distances[sort] > distances[sort+1]) {
					//swap
					sorted = false;
					float temp = distances[sort+1];
					distances[sort+1] = distances[sort];
					distances[sort] = temp;
					
					//swap tris too
					int temp2 = tris.get(sort+1);
					tris.set(sort+1, tris.get(sort));
					tris.set(sort, temp2);
				}
			}
		}
		
		if (distance == Float.MAX_VALUE) {
			LoggingSystem.getLogger().warning(
					"Couldn't detect nearest triangle intersection!");
		} else
			distance = FastMath.sqrt(distance);
		return distance;
	}

	private float getDistanceToTriangle(Vector3f[] triangle) {
		// Transform triangle to world space
		for (int i = 0; i < 3; i++) {
			worldRotation.mult(triangle[i], worldTriangle[i]).multLocal(
					worldScale).addLocal(worldTranslation);
		}
		// Intersection test
		Ray ray = getRay();
		if (ray.intersectWhere(worldTriangle[0], worldTriangle[1],
				worldTriangle[2], intersectionPoint)) {
			return ray.getOrigin().distanceSquared(intersectionPoint);
		}

		// Should not happen
		return Float.MAX_VALUE;
	}
}
